package com.authine.cloudpivot.ext.service.invoice.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapBuilder;
import com.alibaba.cola.dto.RequestContext;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.ext.Enum.*;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.constants.PlatformConst;
import com.authine.cloudpivot.ext.dto.Summary;
import com.authine.cloudpivot.ext.dto.model.bill.Bill;
import com.authine.cloudpivot.ext.dto.model.invoice.*;
import com.authine.cloudpivot.ext.dto.model.settle.SettleInvoice;
import com.authine.cloudpivot.ext.dto.model.settle.SettleInvoiceDetail;
import com.authine.cloudpivot.ext.dto.query.BillQuery;
import com.authine.cloudpivot.ext.dto.query.InvoiceDetailQuery;
import com.authine.cloudpivot.ext.service.invoice.InvoiceDetailBillService;
import com.authine.cloudpivot.ext.utils.*;
import com.authine.mvp.app.launcher.dto.bo.response.BO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class InvoiceDetailBillServiceImpl implements InvoiceDetailBillService {
    @Override
    public void update(List<InvoiceDetail> invoiceDetailList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("根据发票明细修改发票明细核销账单记录");

        List<String> invoiceDetailIds = invoiceDetailList.stream().map(InvoiceDetail::getId).collect(Collectors.toList());
        List<InvoiceDetailBill> invoiceDetailBillList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("invoice_detail_id", invoiceDetailIds)
                .queryForPOJOList(InvoiceDetailBill.class);

        Assert.isTrue(!CollectionUtils.isEmpty(invoiceDetailBillList), "未找到要修改的发票明细核销账单记录");

        for (InvoiceDetail updateParam : invoiceDetailList) {
            BigDecimal updateTotal = updateParam.getTotal();

            List<InvoiceDetailBill> mappingInvoiceDetailBills = invoiceDetailBillList.stream()
                    .filter(t -> t.getInvoice_detail_id().equals(updateParam.getId())).collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.isEmpty(mappingInvoiceDetailBills), "未找到要修改的发票明细核销账单记录");

            //total=明细总额， excludeBalanceTotal=补差总额
            BigDecimal total = BigDecimal.ZERO, excludeBalanceTotal = BigDecimal.ZERO;
            for (InvoiceDetailBill mappingInvoiceDetailBill : mappingInvoiceDetailBills) {
                mappingInvoiceDetailBill.setTax_rate(updateParam.getTax_rate());
                total = BigDecimalUtils.add(total, mappingInvoiceDetailBill.getTotal());

                if (!BillType.Balance.name().equals(mappingInvoiceDetailBill.getBill_type())) {
                    excludeBalanceTotal = BigDecimalUtils.add(excludeBalanceTotal, mappingInvoiceDetailBill.getTotal());
                }
            }

            if (total.compareTo(updateTotal) == 0) {
                continue;
            }

            BigDecimal balanceTotal = BigDecimalUtils.sub(updateTotal, excludeBalanceTotal);
            //删除补差账单,删除已存在的账单发票明细
            List<InvoiceDetailBill> invoiceDetailBills = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                    .eq("invoice_apply_id", updateParam.getInvoice_apply_id())
                    .eq("invoice_detail_id", updateParam.getId())
                    .queryForPOJOList(InvoiceDetailBill.class);
            JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL)
                    .putFieldAndVal("deleted", 1)
                    .in("id", invoiceDetailBills.stream().map(InvoiceDetailBill::getId).collect(Collectors.toList()))
                    .update();
            int updateBillCount = JdbcTemplateUtils.builder(ModelConst.T_BILL)
                    .putFieldAndVal("deleted", 1)
                    .in("id", invoiceDetailBills.stream().map(InvoiceDetailBill::getBill_id).collect(Collectors.toList()))
                    .eq("type", BillType.Balance)
                    .update();
            Assert.isTrue(updateBillCount != 0, "不存在删除的补差账单");

            if (balanceTotal.compareTo(BigDecimal.ZERO) != 0) {
                InvoiceDetailBill detailBill = mappingInvoiceDetailBills.get(0);
                //创建补差单
                Date balanceBillDate = null;
                Bill bill = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                        .eq("id", detailBill.getBill_id())
                        .queryForPOJO(Bill.class);
                if (bill != null) {
                    balanceBillDate = bill.getBill_date();
                }
                Bill createBill = Bill.builder().trade_id(detailBill.getTrade_id()).total(balanceTotal).bill_date(balanceBillDate).build();
                String balanceBillId = billService.createBlanceBill(Collections.singletonList(createBill)).get(0);
                //创建补差账单的开票明细
                InvoiceDetailBill invoiceDetailBill =InvoiceDetailBill.builder()
                                                        .invoice_apply_id(detailBill.getInvoice_apply_id())
                                                        .invoice_detail_id(detailBill.getId())
                                                        .offset_id(null)
                                                        .bill_id(balanceBillId)
                                                        .trade_id(detailBill.getTrade_id())
                                                        .bill_type(BillType.Balance.name())
                                                        .quantity(BigDecimal.ONE)
                                                        .gross_price(balanceTotal)
                                                        .tax_rate(detailBill.getTax_rate())
                                                        .total(balanceTotal)
                                                        .offset_quantity(BigDecimal.ZERO)
                                                        .offset_total(BigDecimal.ZERO).build();
                BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, Collections.singletonList(BeanUtil.beanToMap(invoiceDetailBill)));
            }
        }
        stopWatch.stop();
        String prettyPrint = stopWatch.prettyPrint();
        log.info(prettyPrint);
    }

    /**
     * 根据交易ID查询可开票的账单列表.
     * 注意：由于交易维度需要获取真实已申票金额(非补差)去做可申票金额处理，所以数据可能会返回无账单ID但已申票的账单条目，
     * 如果不需要该条目则按照账单ID!=null进行过滤
     *
     * @param tradeIds 交易ID列表
     * @return 账单维度的可开票model列表 通过trade_id分组求和结果
     */
    public List<InvoiceDetailBill> getCanInvoiceBill(Collection<String> tradeIds){
        List<Bill> billList = billService.list(BillQuery.builder().actual(true).type(BillType.Goods.name()).trade_id(tradeIds).build());
        if (CollectionUtils.isEmpty(billList)) {
            return Collections.emptyList();
        }
        //验证账单的业务类型、组织ID和业务方向是否都一致
        billService.applyRuleValid(billList,true);

        MapBuilder<String, Object> mapBuilder = MapBuilder.create(new HashMap<>());
        //已申请开票账单明细
        StringBuffer invoiceDetailBillSql = new StringBuffer(" select bill.bill_id, IFNULL(sum(bill.quantity-bill.offset_quantity), 0) as quantity, IFNULL(sum(bill.total-bill.offset_total), 0) as total FROM ");
        invoiceDetailBillSql.append(JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL_BILL)).append(" as bill ")
                            .append(" left join ").append(JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_APPLY)).append(" as invoice ")
                            .append(" on invoice.id = bill.invoice_apply_id")
                            .append(" where bill.deleted = 0 ")
                            .append(" and bill.bill_id in (:billIds) and invoice.business_type = :businessType")
                            .append(" group by bill.bill_id");
        mapBuilder.put("billIds", billList.stream().map(Bill::getId).collect(Collectors.toList()));
        mapBuilder.put("businessType", InvoiceBusinessType.Product.name());
        List<InvoiceDetailBill> alreadyInvoiceDetailBills = JdbcTemplateUtils.queryForPOJOList(invoiceDetailBillSql.toString(), mapBuilder.build(), ModelConst.T_INVOICE_DETAIL_BILL, InvoiceDetailBill.class);

        List<InvoiceDetailBill> results = new ArrayList<>();
        for (Bill bill : billList){
            //账单已申请开票数量
            BigDecimal alreadyQuantity = CalculatorUtils.sum(alreadyInvoiceDetailBills.stream()
                    .filter(t -> t.getBill_id().equals(bill.getId())).collect(Collectors.toList()), BiFunctionConst.invoiceBillQuantityFun);
            //可申请数量 = 账单数量-已申请开票
            BigDecimal canApplyQuantity = bill.getQuantity().subtract(alreadyQuantity);
            //这个账单数量是排除了退货的数量，如果只退货完，没有红冲的话，就可能申请数量为负数
            if(canApplyQuantity.compareTo(BigDecimal.ZERO) != 1){
                if (alreadyQuantity.compareTo(BigDecimal.ZERO) != 0) {
                    results.add(InvoiceDetailBill.builder()
                            .trade_id(bill.getTrade_id())
                            .quantity(BigDecimal.ZERO)
                            .total(BigDecimal.ZERO)
                            .already_total(CalculatorUtils.sum(alreadyInvoiceDetailBills.stream().filter(t -> t.getBill_id().equals(bill.getId())).collect(Collectors.toList()), BiFunctionConst.invoiceBillTotalFun))
                            .build());
                }
                continue;
            }
            BigDecimal grossPrice = bill.getPrice(); //商品含税单价
            BigDecimal thisTotal = BigDecimalUtils.mul(canApplyQuantity, grossPrice);// 可申请开票金额 = 含税单价 * 可申请数量
            BigDecimal alreadyTotal = BigDecimal.ZERO;
            if (alreadyQuantity.compareTo(BigDecimal.ZERO)!= 0) {
                alreadyTotal = CalculatorUtils.sum(alreadyInvoiceDetailBills.stream().filter(t -> t.getBill_id().equals(bill.getId())).collect(Collectors.toList()), BiFunctionConst.invoiceBillTotalFun);
                thisTotal = bill.getTotal().subtract(alreadyTotal);
                grossPrice = BigDecimalUtils.div(thisTotal,canApplyQuantity, 6);
            }

            results.add(InvoiceDetailBill.builder()
                    .bill_id(bill.getId())
                    .trade_id(bill.getTrade_id())
                    .bill_type(bill.getType())
                    .quantity(canApplyQuantity)
                    .gross_price(grossPrice)
                    .total(thisTotal)
                    .already_total(alreadyTotal)
                    .tax_rate(bill.getTax_rate())
                    .bill_date(bill.getBill_date())
                    .build());


        }

        return results;
    }

    /**
     * 根据发票明细创建发票明细核销账单记录
     *
     * @param applyParams 发票明细实体列表
     */
    public void create(@NonNull Collection<InvoiceDetail> applyParams) {
        log.info("根据发票明细创建发票明细核销账单记录");

        //判断是否已经存在
        List<String> invoiceDetailIds = applyParams.stream().map(InvoiceDetail::getId).collect(Collectors.toList());
        Integer existsCount = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("count(1)")
                .in("invoice_detail_id", invoiceDetailIds).queryForObject(Integer.class);
        Assert.isTrue(existsCount == 0, "发票明细已核销");

        List<String> tradeIds = applyParams.stream().map(InvoiceDetail::getTrade_id).collect(Collectors.toList());
        List<InvoiceDetailBill> canInvoiceBills = getCanInvoiceBill(tradeIds).stream().filter(t -> null != t.getBill_id()).collect(Collectors.toList());
        Assert.isTrue(!CollectionUtils.isEmpty(canInvoiceBills), "没有可核销发票的明细");

        for (InvoiceDetail applyParam : applyParams) {
            List<InvoiceDetailBill> mappingCanInvoiceBills = canInvoiceBills.stream().filter(t -> t.getTrade_id().equals(applyParam.getTrade_id())).collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.isEmpty(mappingCanInvoiceBills), "没有可申请的账单");

            //过滤只有申请数量大于0的
            mappingCanInvoiceBills = mappingCanInvoiceBills.stream().filter(t -> t.getQuantity().compareTo(BigDecimal.ZERO) == 1).collect(Collectors.toList());

            Date balanceBillDate = mappingCanInvoiceBills.get(0).getBill_date();
            List<InvoiceDetailBill> persist = new ArrayList<>();
            BigDecimal applyQuantity = applyParam.getQuantity(), verifyQuantity = BigDecimal.ZERO, verifyTotal = BigDecimal.ZERO;
            for (InvoiceDetailBill mappingCanInvoiceBill : mappingCanInvoiceBills) {
                if (applyQuantity.compareTo(BigDecimal.ZERO) == 0) {
                    break;
                }

                BigDecimal billQuantity = mappingCanInvoiceBill.getQuantity();
                if (billQuantity.compareTo(applyQuantity) == 1) {
                    billQuantity = applyQuantity;
                }
                applyQuantity = BigDecimalUtils.sub(applyQuantity, billQuantity);

                BigDecimal billTotal = billQuantity.multiply(mappingCanInvoiceBill.getGross_price()).setScale(2, RoundingMode.HALF_UP);
                if (billTotal.compareTo(mappingCanInvoiceBill.getTotal()) == 1) {
                    billTotal = mappingCanInvoiceBill.getTotal();
                }

                persist.add(InvoiceDetailBill.builder()
                        .invoice_apply_id(applyParam.getInvoice_apply_id())
                        .invoice_detail_id(applyParam.getId())
                        .bill_id(mappingCanInvoiceBill.getBill_id())
                        .trade_id(mappingCanInvoiceBill.getTrade_id())
                        .bill_type(mappingCanInvoiceBill.getBill_type())
                        .quantity(billQuantity)
                        .gross_price(mappingCanInvoiceBill.getGross_price())
                        .tax_rate(mappingCanInvoiceBill.getTax_rate())
                        .total(billTotal)
                        .offset_quantity(BigDecimal.ZERO)
                        .offset_total(BigDecimal.ZERO)
                        .build());

                //回写已匹配后的账单数量
                mappingCanInvoiceBill.setQuantity(mappingCanInvoiceBill.getQuantity().subtract(billQuantity));

                verifyQuantity = verifyQuantity.add(billQuantity);
                verifyTotal = verifyTotal.add(billTotal);

            }

            Assert.isTrue(verifyQuantity.compareTo(applyParam.getQuantity()) == 0, "发票明细账单记录数量不等");
            List<Map<String, Object>> boMap = persist.stream().map(BeanUtil::beanToMap).collect(Collectors.toList());
            log.info("发票{}创建的发票账单维度明细\n{}", applyParam.getInvoice_apply_id(), JSON.toJSONString(boMap));
            BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, boMap);

            //判断是否需要进行补差
            if (applyParam.getTotal().compareTo(verifyTotal) != 0) {
                BigDecimal balanceTotal = applyParam.getTotal().subtract(verifyTotal);
                InvoiceDetailBill detailBill = persist.get(0);
                deleteBalance(detailBill.getInvoice_apply_id(), detailBill.getInvoice_detail_id());
                //创建补差单
                Bill bill = Bill.builder()
                        .type(BillType.Balance.name())
                        .trade_id(detailBill.getTrade_id())
                        .total(balanceTotal)
                        .bill_date(balanceBillDate)
                        .build();
                String balanceBillId= billService.createBlanceBill(Collections.singletonList(bill)).get(0);

                //创建补差账单开票明细
                InvoiceDetailBill balanceDetailBill = InvoiceDetailBill.builder()
                        .invoice_apply_id(detailBill.getInvoice_apply_id())
                        .invoice_detail_id(detailBill.getInvoice_detail_id())
                        .bill_id(balanceBillId)
                        .trade_id(detailBill.getTrade_id())
                        .bill_type(BillType.Balance.name())
                        .quantity(BigDecimal.ONE)
                        .gross_price(balanceTotal)
                        .tax_rate(detailBill.getTax_rate())
                        .total(balanceTotal)
                        .offset_quantity(BigDecimal.ZERO)
                        .offset_total(BigDecimal.ZERO)
                        .build();
                BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, Collections.singletonList(BeanUtil.beanToMap(balanceDetailBill)));
            }
        }

    }

    /**
     * 添加服务费开票明细
     * @param applyParams 服务费发票明细列表(交易维度)
     */
    @Override
    public void createService(Collection<InvoiceDetail> applyParams) {
        //判断是否已经存在
        List<String> invoiceDetailIds = applyParams.stream().map(InvoiceDetail::getId).collect(Collectors.toList());
        Integer existsCount = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("count(1)")
                .in("invoice_detail_id", invoiceDetailIds).queryForObject(Integer.class);
        Assert.isTrue(existsCount == 0, "发票明细已核销");

        List<InvoiceDetailBill> canInvoiceBills = new ArrayList<>();

        List<Bill> bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                .in("trade_id", applyParams.stream().map(InvoiceDetail::getTrade_id).collect(Collectors.toSet()))
                .in("type", Arrays.asList(BillType.PlatformFee.name(), BillType.FinancialFee.name()))
                .queryForPOJOList(Bill.class);

        //已申请开票账单明细
        StringBuffer invoiceDetailBillSql = new StringBuffer(" select bill.bill_id, IFNULL(sum(bill.quantity-bill.offset_quantity), 0) as quantity, IFNULL(sum(bill.total-bill.offset_total), 0) as total FROM ");
        invoiceDetailBillSql.append(JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL_BILL)).append(" as bill ");
        invoiceDetailBillSql.append(" left join ").append(JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_APPLY)).append(" as invoice ");
        invoiceDetailBillSql.append(" on invoice.id = bill.invoice_apply_id");
        invoiceDetailBillSql.append(" where bill.deleted = 0 ");
        invoiceDetailBillSql.append(" and bill.bill_id in (:billIds) and invoice.business_type = :businessType");
        invoiceDetailBillSql.append(" group by bill.bill_id");
        Map<String, Object> param = new HashMap<>();
        param.put("billIds", bills.stream().map(Bill::getId).collect(Collectors.toList()));
        param.put("businessType", InvoiceBusinessType.Service.name());
        List<InvoiceDetailBill> alreadyInvoiceDetailBills = JdbcTemplateUtils.queryForPOJOList(invoiceDetailBillSql.toString(), param, ModelConst.T_INVOICE_DETAIL_BILL, InvoiceDetailBill.class);


        for (Bill bill : bills) {
            BigDecimal alreadyTotal = CalculatorUtils.sum(alreadyInvoiceDetailBills.stream()
                    .filter(t -> t.getBill_id().equals(bill.getId())).collect(Collectors.toList()), BiFunctionConst.invoiceBillTotalFun);

            BigDecimal canApplyTotal = BigDecimalUtils.sub(bill.getTotal(), alreadyTotal);
            //如果账单金额大于0，则可申请金额小于等于0时跳过;如果账单金额小于0，则可申请金额必须大于等于0是跳过
            if ((Double.compare(bill.getTotal().doubleValue(), 0.0) == 1 && Double.compare(canApplyTotal.doubleValue(), 0.0) != 1)
                    || (Double.compare(bill.getTotal().doubleValue(), 0.0) == -1 && Double.compare(canApplyTotal.doubleValue(), 0.0) != -1)) {
                continue;
            }

            canInvoiceBills.add(InvoiceDetailBill.builder()
                    .bill_id(bill.getId())
                    .trade_id(bill.getTrade_id())
                    .bill_type(bill.getType())
                    .quantity(BigDecimal.ONE)
                    .gross_price(canApplyTotal)
                    .total(canApplyTotal)
                    .tax_rate(bill.getTax_rate())
                    .build());
        }
        Assert.isTrue(!CollectionUtils.isEmpty(canInvoiceBills), "没有可核销发票的明细");

        for (InvoiceDetail applyParam : applyParams) {
            List<InvoiceDetailBill> mappingCanInvoiceBills = canInvoiceBills.stream().filter(t -> t.getTrade_id().equals(applyParam.getTrade_id())).collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.isEmpty(mappingCanInvoiceBills), "没有可申请的账单");

            Set<String> billIds = new HashSet<>();
            BigDecimal verifyTotal = BigDecimal.ZERO;
            List<InvoiceDetailBill> persist = new ArrayList<>();
            for (InvoiceDetailBill mappingCanInvoiceBill : mappingCanInvoiceBills) {
                persist.add(InvoiceDetailBill.builder()
                        .invoice_apply_id(applyParam.getInvoice_apply_id())
                        .invoice_detail_id(applyParam.getId())
                        .bill_id(mappingCanInvoiceBill.getBill_id())
                        .trade_id(mappingCanInvoiceBill.getTrade_id())
                        .bill_type(mappingCanInvoiceBill.getBill_type())
                        .quantity(mappingCanInvoiceBill.getQuantity())
                        .gross_price(mappingCanInvoiceBill.getGross_price())
                        .tax_rate(mappingCanInvoiceBill.getTax_rate())
                        .total(mappingCanInvoiceBill.getTotal())
                        .offset_quantity(BigDecimal.ZERO)
                        .offset_total(BigDecimal.ZERO)
                        .build());
                verifyTotal = BigDecimalUtils.add(verifyTotal, mappingCanInvoiceBill.getTotal());
                billIds.add(mappingCanInvoiceBill.getBill_id());
            }

            Assert.isTrue(verifyTotal.compareTo(applyParam.getTotal()) == 0, "账单编号【" + String.join(",", billIds) + "】核销发票记录金额 " + verifyTotal + " 和发票申请金额 " + applyParam.getTotal() + " 不相等");
            List<Map<String, Object>> data = persist.stream().map(BeanUtil::beanToMap).collect(Collectors.toList());
            BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, data);
        }
    }

    public void deleteBalance(String invoiceApplyId, String invoiceDetailId){
        List<InvoiceDetailBill> invoiceDetailBills = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("id, bill_id")
                .eq("bill_type", BillType.Balance.name()).eq("invoice_apply_id", invoiceApplyId)
                .eq("invoice_detail_id", invoiceDetailId).eq("deleted", 0).queryForPOJOList(InvoiceDetailBill.class);
        MapBuilder<String, Object> param = MapBuilder.create(new HashMap<>());
        if(invoiceDetailBills.size() == 0){
            return;
        }
        //删除开票明细（补差账单）
        if(!CollectionUtils.isEmpty(invoiceDetailBills)){
            for (InvoiceDetailBill invoiceBill : invoiceDetailBills){
                param.map().clear();
                param.put("id", invoiceBill.getId());
                param.put("deleted", 1);
                BoServiceUtils.updateMainBo( ModelConst.T_INVOICE_DETAIL_BILL, param.build());
            }
        }
        //删除补差账单
        List<String> billIds = invoiceDetailBills.stream().map(InvoiceDetailBill::getBill_id).collect(Collectors.toList());
        billService.delete(billIds);
    }

    /**
     * 根据货物红字发票明细创建红冲发票明细核销账单记录
     *
     * @param applyParams 红字发发票明细实体列表
     */
    public void offsetGoods(@NonNull List<InvoiceDetail> applyParams) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("根据货物红字发票明细创建红冲发票明细核销账单记录");

        //获取正向的蓝字账单核销记录
        List<InvoiceDetailBill> blueInvoiceDetailBillList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("invoice_detail_id", applyParams.stream().map(InvoiceDetail::getOffset_id).collect(Collectors.toList()))
                .queryForPOJOList(InvoiceDetailBill.class);
        Assert.isTrue(!CollectionUtils.isEmpty(blueInvoiceDetailBillList), "未找到要红冲的发票明细账单记录");

        //获取已红冲的发票明细核销账单记录
        List<InvoiceDetailBill> alreadyOffsetList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("offset_id", blueInvoiceDetailBillList.stream().map(InvoiceDetailBill::getId).collect(Collectors.toList()))
                .queryForPOJOList(InvoiceDetailBill.class);

        for (InvoiceDetail applyParam : applyParams) {
            //蓝票的明细账单
            List<InvoiceDetailBill> mappingInvoiceDetailBills = blueInvoiceDetailBillList.stream()
                    .filter(t -> t.getInvoice_detail_id().equals(applyParam.getOffset_id()) &&  !BillType.Balance.name().equals(t.getBill_type())).collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.isEmpty(mappingInvoiceDetailBills), "未找到要红冲的发票明细账单记录");

            //根据发票的交易明细创建红冲的账单明细
            //红冲总额
            BigDecimal offsetTotal = BigDecimal.ZERO, applyOffsetQuantity = applyParam.getQuantity();
            for (InvoiceDetailBill blueInvoiceDetailBill : mappingInvoiceDetailBills){
                if(applyParam.getQuantity().compareTo(BigDecimal.ZERO) == 0){
                    break;
                }
                //蓝票明细账单对应的红冲明细账单
                List<InvoiceDetailBill> mappingOffsetList = alreadyOffsetList.stream().filter(t -> t.getOffset_id().equals(blueInvoiceDetailBill.getInvoice_detail_id())).collect(Collectors.toList());
                BigDecimal alreadyOffsetTotal = CalculatorUtils.sum(mappingOffsetList, BiFunctionConst.invoiceBillTotalFun), //账单维度已红冲总额
                        alreadyOffsetQuantity = CalculatorUtils.sum(mappingOffsetList, BiFunctionConst.invoiceBillQuantityFun);//账单维度已红冲数量
                //跳过已全部红冲的明细
                BigDecimal canOffsetQuantity = BigDecimalUtils.sub(blueInvoiceDetailBill.getQuantity(), alreadyOffsetQuantity);
                if (canOffsetQuantity.compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }
                //默认红冲数量为剩余可红冲数量，数量变更则表示手动部分红冲
                BigDecimal offsetQuantity = canOffsetQuantity;
                if (offsetQuantity.compareTo(applyOffsetQuantity) == 1) {
                    offsetQuantity = applyParam.getQuantity();
                }

                //最后一次红冲，拿总金额跟数量反算单价
                BigDecimal grossPrice = blueInvoiceDetailBill.getGross_price();
                offsetTotal = BigDecimalUtils.round(BigDecimalUtils.mul(offsetQuantity, grossPrice), 2);
                if (offsetQuantity.compareTo(canOffsetQuantity) == 0) {
                    offsetTotal = BigDecimalUtils.sub(blueInvoiceDetailBill.getTotal(), alreadyOffsetTotal);
                    grossPrice = BigDecimalUtils.div(offsetTotal, offsetQuantity,6);
                }

                InvoiceDetailBill offsetInvoiceDetailBill = InvoiceDetailBill.builder()
                        .invoice_apply_id(applyParam.getInvoice_apply_id())
                        .invoice_detail_id(applyParam.getId())
                        .offset_id(blueInvoiceDetailBill.getId())
                        .bill_id(blueInvoiceDetailBill.getBill_id())
                        .trade_id(blueInvoiceDetailBill.getTrade_id())
                        .bill_type(blueInvoiceDetailBill.getBill_type())
                        .quantity(offsetQuantity)
                        .gross_price(grossPrice)
                        .tax_rate(blueInvoiceDetailBill.getTax_rate())
                        .total(offsetTotal)
                        .offset_quantity(BigDecimal.ZERO)
                        .offset_total(BigDecimal.ZERO)
                        .build();
                Map<String, Object> createMap = BeanUtil.beanToMap(offsetInvoiceDetailBill);
                BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, Collections.singletonList(createMap));

                blueInvoiceDetailBill.setOffset_quantity(BigDecimalUtils.add(alreadyOffsetQuantity, offsetInvoiceDetailBill.getOffset_quantity()));
                blueInvoiceDetailBill.setOffset_total(BigDecimalUtils.add(alreadyOffsetTotal, offsetInvoiceDetailBill.getTotal()));
                BoServiceUtils.updateMainBo( ModelConst.T_INVOICE_DETAIL_BILL, Collections.singletonList(BeanUtil.beanToMap(blueInvoiceDetailBill)));
            }

            //判断是否需要进行补差金额红冲
            if (applyParam.getTotal().compareTo(offsetTotal) != 0) {
                List<InvoiceDetailBill> mappingInvoiceDetailBalanceBills = blueInvoiceDetailBillList.stream()
                        .filter(t -> t.getInvoice_detail_id().equals(applyParam.getOffset_id()) && BillType.Balance.name().equals(t.getBill_type())).collect(Collectors.toList());
                Assert.isTrue(mappingInvoiceDetailBalanceBills.size() == 1, "未找到要红冲补差类型的发票明细账单记录");

                BigDecimal balanceTotal = BigDecimalUtils.sub(applyParam.getTotal(), offsetTotal);
                InvoiceDetailBill detailBill = mappingInvoiceDetailBalanceBills.get(0);
                InvoiceDetailBill blanceDetailBill = InvoiceDetailBill.builder()
                        .invoice_apply_id(applyParam.getInvoice_apply_id())
                        .invoice_detail_id(applyParam.getId())
                        .offset_id(detailBill.getId())
                        .bill_id(detailBill.getBill_id())
                        .trade_id(detailBill.getTrade_id())
                        .bill_type(BillType.Balance.name())
                        .quantity(BigDecimal.ONE)
                        .gross_price(balanceTotal)
                        .tax_rate(detailBill.getTax_rate())
                        .total(balanceTotal)
                        .offset_quantity(BigDecimal.ZERO)
                        .offset_total(BigDecimal.ZERO)
                        .build();
                //创建补差账单
                Map<String, Object> createMap = BeanUtil.beanToMap(blanceDetailBill);
                BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, Collections.singletonList(createMap));
            }
        }

        stopWatch.stop();
        String prettyPrint = stopWatch.prettyPrint();
        log.info(prettyPrint);
    }

    /**
     * 根据服务费发票明细账单红冲服务费
     * @param invoice 发票申请单
     * @param offsetInvoiceDetails 服务费红冲对应的发票明细账单（invoice_detail_bill）
     * @return
     */
    public InvoiceApply offsetService(@NonNull InvoiceApply invoice, Collection<OffsetInvoiceDetail> offsetInvoiceDetails) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("该方法不会持久化服务费账单只构建要生成的服务费发票核销账单明细记录,并回写对应的蓝票核销账单明细已红冲金额");

        List<InvoiceDetailBill> invoiceDetailBillList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("invoice_detail_id", offsetInvoiceDetails.stream().map(OffsetInvoiceDetail::getId).collect(Collectors.toList()))
                .eq("invoice_apply_id", invoice.getId())
                .queryForPOJOList(InvoiceDetailBill.class);
        Assert.isTrue(!CollectionUtils.isEmpty(invoiceDetailBillList), "未找到要红冲的发票明细核销账单记录");

        //获取已红冲的发票明细核销账单记录
        List<InvoiceDetailBill> alreadyOffsetList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("offset_id", invoiceDetailBillList.stream().map(InvoiceDetailBill::getId).collect(Collectors.joining(",")))
                .queryForPOJOList(InvoiceDetailBill.class);


        BigDecimal invoiceOffsetTotal = BigDecimal.ZERO;
        List<String> invoiceDetailIds = new ArrayList<>();
        List<InvoiceDetailBill> persist = new ArrayList<>();
        for (InvoiceDetailBill invoiceDetailBill : invoiceDetailBillList) {
            BigDecimal alreadyOffsetTotal = CalculatorUtils.sum(alreadyOffsetList.stream()
                    .filter(t -> t.getOffset_id().equals(invoiceDetailBill.getId())).collect(Collectors.toList()), BiFunctionConst.invoiceBillTotalFun);

            BigDecimal offsetTotal = BigDecimalUtils.sub(invoiceDetailBill.getTotal(), alreadyOffsetTotal);
            //如果有明细，则按明细的红冲金额来红冲
            if (!CollectionUtils.isEmpty(offsetInvoiceDetails)) {
                String errorMessage = "蓝票账单明细编号【" + invoiceDetailBill.getId() + "】";
                Assert.isTrue(offsetTotal.compareTo(BigDecimal.ZERO) == 1, errorMessage + "可申请金额为0");

                List<OffsetInvoiceDetail> applyParams = offsetInvoiceDetails.stream().filter(t -> t.getId().equals(invoiceDetailBill.getInvoice_detail_id())).collect(Collectors.toList());
                Assert.isTrue(applyParams.size() == 1, errorMessage + "红冲参数必须唯一");

                OffsetInvoiceDetail detail = applyParams.get(0);
                Assert.notNull(detail.getTotal(), errorMessage + "红冲金额不能为空");
                Assert.isTrue(detail.getTotal().compareTo(BigDecimal.ZERO) == 1, errorMessage + "红冲金额不正确");
                Assert.isTrue(detail.getTotal().compareTo(offsetTotal) != 1, errorMessage + "红冲金额【" + detail.getTotal() + "】不可大于可红冲金额【" + offsetTotal + "】");

                offsetTotal = detail.getTotal();
            }

            if (offsetTotal.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }

            //构建红冲明细
            InvoiceDetailBill offsetInvoiceDetail = InvoiceDetailBill.builder()
                    //.invoice_apply_id(invoiceApplyId)
                    .quantity(BigDecimal.ONE)
                    .offset_id(invoiceDetailBill.getId())
                    .bill_id(invoiceDetailBill.getBill_id())
                    .trade_id(invoiceDetailBill.getTrade_id())
                    .bill_type(invoiceDetailBill.getBill_type())
                    .gross_price(offsetTotal)
                    .tax_rate(invoiceDetailBill.getTax_rate())
                    .total(offsetTotal)
                    .offset_quantity(BigDecimal.ZERO)
                    .offset_total(BigDecimal.ZERO)
                    .build();
            persist.add(offsetInvoiceDetail);
            invoiceDetailIds.add(invoiceDetailBill.getInvoice_detail_id());

            invoiceDetailBill.setOffset_total(BigDecimalUtils.add(alreadyOffsetTotal, offsetInvoiceDetail.getTotal()));
            Map<String, Object> updateParam = new HashMap<>();
            updateParam.put("id", invoiceDetailBill.getId());
            updateParam.put("offset_total", invoiceDetailBill.getOffset_total());
            BoServiceUtils.updateMainBo( ModelConst.T_INVOICE_DETAIL_BILL, updateParam);

            invoiceOffsetTotal = BigDecimalUtils.add(invoiceOffsetTotal, offsetInvoiceDetail.getTotal());
        }
        Assert.isTrue(!CollectionUtils.isEmpty(persist), "不存在可红冲的发票明细核销账单记录");

        //构建红字发票单
        InvoiceApply offsetInvoiceApply = InvoiceApply.builder()
                .total(invoiceOffsetTotal)
                .apply_member_id(RequestContext.getCorpLoginId())
                .apply_member_name(RequestContext.getLoginRealName())
                .statuss(InvoiceStatus.DrawerConfirming.name())
                .price_amount(BigDecimal.ZERO)
                .tax_amount(BigDecimal.ZERO)
                .written_off_amount(BigDecimal.ZERO)
                .offset_id(invoice.getId())
                .type(invoice.getType())
                .business_type(InvoiceBusinessType.getRedType(invoice.getBusiness_type()).name())
                .purchase_name(invoice.getPurchase_name())
                .purchase_number(invoice.getPurchase_number())
                .purchase_address(invoice.getPurchase_address())
                .purchase_phone(invoice.getPurchase_phone())
                .purchase_bank(invoice.getPurchase_bank())
                .purchase_account(invoice.getPurchase_account())
                .receiver(invoice.getReceiver())
                .phone(invoice.getPhone())
                .address(invoice.getAddress())
                .purchase_organization_id(invoice.getPurchase_organization_id())
                .purchase_organization_name(invoice.getPurchase_organization_name())
                .sale_organization_id(invoice.getSale_organization_id())
                .sale_organization_name(invoice.getSale_organization_name())
                .extends_attr(InvoiceExtendsAttr.conversion(InvoiceExtendsAttr.builder().taxRate(PlatformConst.PLATTAXRATE.toString()).build()).get())
                .build();
        Map<String, Object> param = BeanUtil.beanToMap(offsetInvoiceApply);
        param.put("statuss", InvoiceStatus.DrawerConfirming.name());
        SingleResponse<BO> boSingleResponse=BoServiceUtils.createBo(ModelConst.T_INVOICE_APPLY, Collections.singletonList(BeanUtil.beanToMap(offsetInvoiceApply)));
        String invoiceApplyId = boSingleResponse.getData().getBizObjectId();
        //红冲交易发票明细
        for (InvoiceDetailBill bill : persist){
            bill.setInvoice_apply_id(invoiceApplyId);
        }
        persist = offsetServiceDetail(invoice.getId(), invoiceDetailIds, persist);
        BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL_BILL, persist.stream().map(BeanUtil::beanToMap).collect(Collectors.toList()));

        stopWatch.stop();
        String prettyPrint = stopWatch.prettyPrint();
        log.info(prettyPrint);

        return offsetInvoiceApply;
    }

    /**
     * 创建服务费红冲的invoice_detail
     * @param invoiceId 源蓝票发票单ID
     * @param invoiceDetailIds 源蓝票明细ID
     * @param invoiceDetailBillList 红冲的发票明细账单列表（服务费红冲以发票明细账单维度红冲）
     * @return
     */
    public List<InvoiceDetailBill> offsetServiceDetail(@NonNull String invoiceId, @Nullable Collection<String> invoiceDetailIds, @NonNull List<InvoiceDetailBill> invoiceDetailBillList) {
        //根据发票单ID(必填)和发票明细ID(可选)查询需要红冲的服务费发票明细
        InvoiceDetailQuery query = InvoiceDetailQuery.builder()
                .canOffsetTotal(true)
                .ids(invoiceDetailIds)
                .businessTypes(Arrays.asList(InvoiceBusinessType.RedService.name(), InvoiceBusinessType.Service.name()))
                .invoiceApplyIds(Collections.singletonList(invoiceId))
                .build();
        List<InvoiceDetail> detailList = invoiceDetailService.listInvoiceDetail(query);
        Assert.isTrue(!CollectionUtils.isEmpty(detailList), "未获取到可红冲的发票明细信息");

        //根据查询到的服务费发票明细查询对应已红冲的服务费发票明细
        InvoiceDetailQuery offsetInvoiceDetailQuery = InvoiceDetailQuery.builder()
                .canOffsetTotal(true)
                .offsetIds(detailList.stream().map(InvoiceDetail::getId).collect(Collectors.toList()))
                .invoiceStatuses(InvoiceStatus.applied())
                .businessTypes(Collections.singletonList(InvoiceBusinessType.RedService.name()))
                .build();
        List<InvoiceDetail> alreadyOffsetList = invoiceDetailService.listInvoiceDetail(offsetInvoiceDetailQuery);

        List<InvoiceDetailBill> results = new ArrayList<>();

        //构建红冲服务费发票明细并累计出红字发票单的含税、未税、税额
        for (InvoiceDetail detail : detailList) {
            List<InvoiceDetail> mappingOffsetList = alreadyOffsetList.stream().filter(t -> t.getOffset_id().equals(detail.getId())).collect(Collectors.toList());
            BigDecimal alreadyOffsetTotal = CalculatorUtils.sum(mappingOffsetList, BiFunctionConst.invoiceDetailTotalFun);
            //当前明细不存在可申请金额则跳过
            BigDecimal canOffsetTotal = BigDecimalUtils.sub(detail.getTotal(), alreadyOffsetTotal);

            List<InvoiceDetailBill> mappingInvoiceDetailBillList = invoiceDetailBillList.stream().filter(t -> t.getTrade_id().equals(detail.getTrade_id())).collect(Collectors.toList());
            Assert.isTrue(!CollectionUtils.isEmpty(mappingInvoiceDetailBillList), "未获取到可红冲明细");

            //Long invoiceDetailId = ids.get(index);
            BigDecimal applyOffsetTotal =  CalculatorUtils.sum(mappingInvoiceDetailBillList, BiFunctionConst.invoiceBillTotalFun);

            Assert.isTrue(applyOffsetTotal.compareTo(canOffsetTotal) != 1, "红冲金额大于可红冲金额");

            BigDecimal beforeTaxAmount = BigDecimalUtils.round(TaxRateUtils.calculateTaxBefore(applyOffsetTotal, detail.getTax_rate()), 2);
            InvoiceDetail offsetInvoiceDetail = InvoiceDetail.builder()
                    .invoice_apply_id(mappingInvoiceDetailBillList.get(0).getInvoice_apply_id())
                    .offset_id(detail.getId())
                    .trade_id(detail.getTrade_id())
                    .name(detail.getName())
                    .model_spec(detail.getModel_spec())
                    .unit(detail.getUnit())
                    .quantity(BigDecimal.ONE)
                    .tax_rate(detail.getTax_rate())
                    .total(applyOffsetTotal)
                    .amount(beforeTaxAmount)
                    .tax(BigDecimalUtils.sub(applyOffsetTotal, beforeTaxAmount))
                    .offset_quantity(BigDecimal.ZERO)
                    .offset_total(BigDecimal.ZERO)
                    .price(beforeTaxAmount)
                    .gross_price(applyOffsetTotal)
                    .extends_attr(InvoiceDetailExtendsAttr.offset(detail.getExtends_attr()))
                    .build();
            detail.setOffset_quantity(BigDecimal.ONE).setOffset_total(BigDecimalUtils.add(offsetInvoiceDetail.getTotal(), alreadyOffsetTotal));
            //更新源开票明细
            BoServiceUtils.updateMainBo( ModelConst.T_INVOICE_DETAIL, BeanUtil.beanToMap(detail));
            //持久化红冲开票明细
            SingleResponse<BO> singleResponse = BoServiceUtils.createBo(ModelConst.T_INVOICE_DETAIL, Collections.singletonList(BeanUtil.beanToMap(offsetInvoiceDetail)));
            String bizObjectId = singleResponse.getData().getBizObjectId();
            for (InvoiceDetailBill mappingInvoiceBill : mappingInvoiceDetailBillList){
                results.add(mappingInvoiceBill.setInvoice_detail_id(bizObjectId));
            }
        }
        return results;
    }

    @Override
    public SettleInvoice getSettleInvoice(SettleApplyType applyType, Collection<String> ids) {
        log.info(">>>>>>开始获取可结算发票数据>>>>查询类型{}，ids列表{}", applyType.name(), StringUtils.join(ids, ","));
        List<Bill> bills;
        if(SettleApplyType.Invoice == applyType){
            List<InvoiceApply> invoiceApplyList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_APPLY).selectField("*")
                    .in("id", ids)
                    .queryForPOJOList(InvoiceApply.class);
            Assert.isTrue(!CollectionUtils.isEmpty(invoiceApplyList), "根据发票id获取可结算数据异常");
            //销售发票
            Set<String> saleOrganizationIdCollect = invoiceApplyList.stream()
                    .filter(t -> PlatformConst.PLATFORM_ORGANIZATION_IDS.contains(t.getSale_organization_id().getId())).map(t->t.getSale_organization_id().getId()).collect(Collectors.toSet());
            //采购发票
            Set<String> purchaseOrganizationIdCollect = invoiceApplyList.stream()
                    .filter(t -> PlatformConst.PLATFORM_ORGANIZATION_IDS.contains(t.getPurchase_organization_id().getId()))
                    .map(t->t.getPurchase_organization_id().getId())
                    .collect(Collectors.toSet());
            //验证销售发票
            if (!CollectionUtils.isEmpty(saleOrganizationIdCollect) && CollectionUtils.isEmpty(purchaseOrganizationIdCollect)) {
                Assert.isTrue(saleOrganizationIdCollect.size() == 1, "销售发票结算时，平台组织不唯一");
            }
            //验证采购发票
            else if (CollectionUtils.isEmpty(saleOrganizationIdCollect) && !CollectionUtils.isEmpty(purchaseOrganizationIdCollect)) {
                Assert.isTrue(purchaseOrganizationIdCollect.size() == 1, "采购发票结算时，平台组织不唯一");
            } else {
                log.info("按发票核销，平台组织验证异常，销售组织" + String.join(",", saleOrganizationIdCollect) + "采购组织" + String.join(",", purchaseOrganizationIdCollect));
                throw new IllegalArgumentException("平台组织验证异常，销售组织" + String.join(",", saleOrganizationIdCollect) + "采购组织" + String.join(",", purchaseOrganizationIdCollect));
            }
            List<String>  billIds = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("bill_id")
                    .in("invoice_apply_id", ids)
                    .queryForList(String.class);
            Assert.isTrue(!CollectionUtils.isEmpty(billIds), "发票编号为：" + ids + "未获取到对应账单，请检查该发票是否已核销交易记录");
            bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                    .in("id", billIds)
                    .queryForPOJOList(Bill.class);
        }else if (SettleApplyType.VENDOR_ORDER == applyType) {
            //需要采购账单和补差账单
            bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                    .in("vendor_order_id", ids)
                    .eq("direction", TradeDirection.Purchase.name())
                    .in("type", Arrays.asList(BillType.Goods.name(),BillType.Balance.name()))
                    .queryForPOJOList(Bill.class);
        } else if (SettleApplyType.ORDER == applyType){
            //需要销售账单和补差账单
            bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                    .in("customer_order_id", ids)
                    .eq("direction", TradeDirection.Sale.name())
                    .queryForPOJOList(Bill.class);
        }else if (SettleApplyType.Trade == applyType) {
            //需要采购账单和服务费账单
            bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                    .in("trade_id", ids)
                    .eq("direction", TradeDirection.Purchase.name())
                    .in("type", Arrays.asList(BillType.Goods.name(),BillType.Balance.name()))
                    .queryForPOJOList(Bill.class);
        }else if(SettleApplyType.ORDER_GOODS == applyType){
            bills = JdbcTemplateUtils.builder(ModelConst.T_BILL).selectField("*")
                    .in("order_goods_id", ids)
                    .eq("direction", TradeDirection.Sale.name())
                    .queryForPOJOList(Bill.class);
        }else{
            return null;
        }
        Assert.isTrue(!CollectionUtils.isEmpty(bills), "未获取到相应可结算账单");
        //可以一起申请的账单类型
        Set<BillType> billTypeSet = bills.stream().map(t -> Enum.valueOf(BillType.class, t.getType())).collect(Collectors.toSet());
        boolean billCanTogether = (billTypeSet.contains(BillType.Balance) && billTypeSet.contains(BillType.Goods)) ||
                (billTypeSet.contains(BillType.PlatformFee) && billTypeSet.contains(BillType.FinancialFee)) ||
                (billTypeSet.contains(BillType.FinancialFeeRefund) && billTypeSet.contains(BillType.PlatformFeeRefund));
        Assert.isTrue(billTypeSet.size() == 1 || (billTypeSet.size() > 1 && billCanTogether), "账单类型获取不正确，共获取" + billTypeSet.size() + "个");


        List<TradeOrderType> billTradeOrderTypeList = bills.stream().map(t -> Enum.valueOf(TradeOrderType.class, t.getBusiness_type())).distinct().collect(Collectors.toList());
        Assert.isTrue(!CollectionUtils.isEmpty(billTradeOrderTypeList) && billTradeOrderTypeList.size() == 1, "普通订单跟代购订单不能一起结算");
        //根据账单获取已开票的发票明细
        List<String> billIds = bills.stream().map(Bill::getId).collect(Collectors.toList());

        List<InvoiceDetailBill> invoiceDetailBillEntityList = new ArrayList<>();//账单维度开票明细
        String invoiceDetailBillSql = "select db.* from i_financialcenter_t_invoice_detail_bill db \n" +
                " LEFT JOIN i_financialcenter_t_invoice_apply apply ON db.invoice_apply_id = apply.id \n" +
                " where apply.business_type=:businessType \n" +
                " and db.offset_total <> db.total"+
                " and db.bill_id in (:billIds)";
        //普通订单
        if (billTradeOrderTypeList.get(0) == TradeOrderType.Ordinary) {
            billService.applyRuleValid(bills, false);
            Map<String, Object> param = MapBuilder.create(new HashMap<String, Object>())
                    .put("billIds", billIds)
                    .put("businessType", InvoiceBusinessType.Product.name()).build();
            if(SettleApplyType.Invoice == applyType){
                invoiceDetailBillSql = invoiceDetailBillSql + " and apply.id in (:invoiceApplyId)";
                param.put("invoiceApplyId", ids);
            }
            invoiceDetailBillEntityList = JdbcTemplateUtils.queryForPOJOList(invoiceDetailBillSql, param, ModelConst.T_INVOICE_DETAIL_BILL, InvoiceDetailBill.class);

        } else if (billTradeOrderTypeList.get(0) == TradeOrderType.Agent) {
            //判断是否是代购服务费
            String businessType = "";
            if (!CollectionUtils.isEmpty(billTypeSet) && billTypeSet.size() == 1 &&
                    (billTypeSet.contains(BillType.FinancialFee) || billTypeSet.contains(BillType.PlatformFee))) {
                //代购服务费
                Assert.isTrue(BillType.getFee().containsAll(billTypeSet), "必须是服务费类型账单才可操作");
                businessType = InvoiceBusinessType.Service.name();
            } else {
                //代购商品
                billService.applyRuleValid(bills, false);
                businessType = InvoiceBusinessType.Product.name();
            }
            Map<String, Object> param = MapBuilder.create(new HashMap<String, Object>())
                    .put("billIds", billIds)
                    .put("businessType", businessType).build();
            if(SettleApplyType.Invoice == applyType){
                invoiceDetailBillSql = invoiceDetailBillSql + " and apply.id=:invoiceApplyId";
                param.put("invoiceApplyId", ids);
            }
            invoiceDetailBillEntityList = JdbcTemplateUtils.queryForPOJOList(invoiceDetailBillSql, param, ModelConst.T_INVOICE_DETAIL_BILL, InvoiceDetailBill.class);
        } else {
            log.info("账单业务类型异常，账单编号" + StringUtils.join(billIds, ","));
            Assert.isTrue(false, "根据不同的ID类型获取对应的可结算的发票数据，账单业务类型异常");
        }

        List<SettleInvoiceDetail> settleInvoiceDetails = new ArrayList<>();

        if (!CollectionUtils.isEmpty(invoiceDetailBillEntityList)) {
            Map<String, List<InvoiceDetailBill>> invoiceDetailsMap = invoiceDetailBillEntityList.stream().collect(Collectors.groupingBy(InvoiceDetailBill::getBill_id));

            //发票明细对应的账单
            List<InvoiceApply> invoiceApplyList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_APPLY).selectField("*")
                    .in("id", invoiceDetailBillEntityList.stream().map(InvoiceDetailBill::getInvoice_apply_id).collect(Collectors.toSet()))
                    .queryForPOJOList(InvoiceApply.class);
            Set<InvoiceBusinessType> invoiceBusinessTypeSet = invoiceApplyList.stream().map(t -> Enum.valueOf(InvoiceBusinessType.class, t.getBusiness_type())).collect(Collectors.toSet());
            Assert.isTrue(!CollectionUtils.isEmpty(invoiceBusinessTypeSet) && invoiceBusinessTypeSet.size() == 1, "账单对应发票的业务类型不正确");

            //账单是商品账单或补差账单，发票是货物发票
            boolean goodsFlag = (billTypeSet.contains(BillType.Goods) || billTypeSet.contains(BillType.Balance))
                    && invoiceBusinessTypeSet.contains(InvoiceBusinessType.Product);
            //账单是平台服务费账单或垫资服务费账单，发票是服务费发票
            boolean serviceFlag = (billTypeSet.contains(BillType.PlatformFee) || billTypeSet.contains(BillType.FinancialFee)) && invoiceBusinessTypeSet.contains(InvoiceBusinessType.Service);
            Assert.isTrue(goodsFlag || serviceFlag, "账单对应的发票业务类型不一致");

            invoiceDetailsMap.forEach((billIdKey, invoiceDetailsValue) -> {

                //通过发票账单明细构建结算明细
                List<SettleInvoiceDetail> tempSettleInvoiceDetails = new ArrayList<>();
                BigDecimal invoicedTotal = BigDecimal.ZERO;
                for (InvoiceDetailBill invoiceDetailBill : invoiceDetailsValue) {
                    invoicedTotal = BigDecimalUtils.add(invoicedTotal, invoiceDetailBill.getTotal());

                    InvoiceApply invoice = invoiceApplyList.stream()
                            .filter(t -> t.getId().equals(invoiceDetailBill.getInvoice_apply_id())).collect(Collectors.toList()).get(0);

                    Bill bill = bills.stream().filter(t -> t.getId().equals(invoiceDetailBill.getBill_id())).findAny().orElseThrow(RuntimeException::new);
                    Summary summary = Summary.conversion(bill.getSummary());
                    tempSettleInvoiceDetails.add(SettleInvoiceDetail.builder()
                            .apply_type(applyType)
                            .bill_id(billIdKey)
                            .trade_id(invoiceDetailBill.getTrade_id())
                            .invoice_apply_id(invoiceDetailBill.getInvoice_apply_id())
                            .invoice_detail_bill_id(invoiceDetailBill.getId())
                            .total(invoiceDetailBill.getTotal())
                            .name(summary.getName())
                            .number(invoice.getNumber())
                            .date(invoice.getDate())
                            .model_spec(summary.getModel() + " " + summary.getSpec())
                            .platformservicefee(BigDecimal.ZERO)
                            .interests(BigDecimal.ZERO)
                            .build());
                }

                Bill bill = bills.stream().filter(t -> t.getId().equals(billIdKey)).collect(Collectors.toList()).get(0);

                //保存账单维度的已开票金额
                for (SettleInvoiceDetail settleInvoiceDetail : tempSettleInvoiceDetails) {
                    String applyId;
                    switch (applyType) {
                        case Trade:
                            applyId = bill.getTrade_id();
                            break;
                        case VENDOR_ORDER:
                            applyId = bill.getVendor_order_id();
                            break;
                        default:
                            applyId = settleInvoiceDetail.getInvoice_apply_id();
                            break;
                    }
                    settleInvoiceDetails.add(settleInvoiceDetail.setBill_total(invoicedTotal).setApply_id(applyId));
                }
            });

            InvoiceApply uniqueVerify = invoiceApplyList.get(0);
            for (InvoiceApply invoiceApply : invoiceApplyList) {
                Assert.isTrue(invoiceApply.getSale_organization_id().equals(uniqueVerify.getSale_organization_id()),"销售组织必须唯一");
            }

            List<String> purchaseNameList = invoiceApplyList.stream().map(InvoiceApply::getPurchase_name).distinct().collect(Collectors.toList());
            String purchaseNames = StringUtils.join(purchaseNameList, ",");

            log.info(">>>>>>结算获取可结算发票数据>>>>查询类型{}，ids列表{}", applyType.name(), StringUtils.join(ids, ","));

            /*
             * 返回的可结算的发票数据：
             *  采购组织id、采购组织名称、销售组织id，销售组织名称
             *  结算明细：申请ID、账单维度已开票金额
             *  采购组织抬头
             */
            return SettleInvoice.builder()
                    .purchase_organization_id(uniqueVerify.getPurchase_organization_id())
                    .purchase_organization_name(uniqueVerify.getPurchase_organization_name())
                    .sale_organization_id(uniqueVerify.getSale_organization_id())
                    .sale_organization_name(uniqueVerify.getSale_organization_name())
                    .settleInvoiceDetails(settleInvoiceDetails)
                    .purchase_name(purchaseNames)
                    .build();
        }
        return null;
    }

    public void remove(Collection<String> invoiceDetailIds){
        List<InvoiceDetailBill> invoiceDetailBillList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .eq("deleted", 0).in("invoice_detail_id", invoiceDetailIds)
                .queryForPOJOList(InvoiceDetailBill.class);
        if (CollectionUtils.isEmpty(invoiceDetailBillList)) {
            return;
        }
        for (InvoiceDetailBill invoiceDetailBill : invoiceDetailBillList) {
            BoServiceUtils.deleteBo(ModelConst.T_INVOICE_DETAIL_BILL, invoiceDetailBill.getId(), true);
        }
    }
}
